/**
 * DSM - Distributed Search Manager
 * Developed by Milspec Research International Pty Ltd
 * $Author: skahl $
 * $Revision: 1.13 $
 * $Date: 2007/12/05 22:28:06 $
 * (c)Copyright 2004
 * education.au limited
 * DEST
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * Neither the names education.au limited, DEST nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 * To the extent permitted by law, the copyright owners of this software and
 * its contributors:
 * (i) exclude all warranties in relation to the software; and
 * (ii) exclude liability for all losses, costs, expenses and damages arising
 * in any way from the use of the software whether arising from or in
 * relation to breach of contract, negligence or any other tort, in equity
 * or otherwise. If the software is in breach of a warranty which is
 * implied by law, the copyright owners and contributors liability is
 * limited to the replacement of the software.
 *
 * @author	gsingh
 */

package au.edu.educationau.opensource.dsm;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import au.edu.educationau.opensource.dsm.cache.DiskCache;
import au.edu.educationau.opensource.dsm.obj.SearchTermHistory;
import au.edu.educationau.opensource.dsm.service.DBConnectionService;
import au.edu.educationau.opensource.dsm.service.PropertiesService;
import au.edu.educationau.opensource.dsm.service.SOAPSearchResultCacheService;
import au.edu.educationau.opensource.dsm.service.SearchManagerService;
import au.edu.educationau.opensource.dsm.service.SearchResultService;
import au.edu.educationau.opensource.dsm.service.SpellCheckService;
import au.edu.educationau.opensource.dsm.threadpool.ThreadPool;
import au.edu.educationau.opensource.dsm.worker.SearchJob;

public final class DeusExMachina {
	static Logger logger = Logger.getLogger(DeusExMachina.class.getName());

	/**
	 * Number of threads available in the threadpool. 
	 */
	public static final int THREAD_POOL_SIZE = 250;

	private static Random rand = new Random();

	private static DSMCore dsmCore;

	/**
	 * Instance of this application. Could be used to make the staging version
	 * with different parameters
	 */
	public static final String APP_INSTANCE = System.getProperty("dsm.instance");

	/**
	 * This method is more of a finder type method where it can be called from a
	 * jsp or servlet for the classloader to wake up and load this app.
	 */
	public static void init() {
	} // Class Loader is in business

	static {
		dsmCore = DSMCoreFactory.getInstance();
	}

	/**
	 * Generates a token using the Random class as a long number signed.
	 */
	public static long generateToken() {
		return rand.nextLong();
	}

	/** The ThreadPool Instance that this application uses */
	public static ThreadPool _ThreadPool() {
		return (ThreadPool) dsmCore.getService(DSMCore.THREAD_POOL);
	}

	/** The DBConnection service that this application uses */
	public static DBConnectionService _DBConnectionService() {
		return (DBConnectionService) dsmCore.getService(DSMCore.DB_CONNECTION);
	}

	/** The Disk cache service that this application uses */
	public static DiskCache _DiskCache() {
		return (DiskCache) dsmCore.getService(DSMCore.DISK_CACHE);
	}

	/** The Search Manager Service singleton instance itself */
	public static SearchManagerService _SearchManagerService() {
		return (SearchManagerService) dsmCore.getService(DSMCore.SEARCH_MANAGER);
	}

	/** The Search Result Service singleton instance itself */
	public static SearchResultService _SearchResultService() {
		return (SearchResultService) dsmCore.getService(DSMCore.SEARCH_RESULTS);
	}

	/** The SOAP Search Result cache to hold SOAP gotten search results singleton */
	public static SOAPSearchResultCacheService _SOAPSearchResultCacheService() {
		return (SOAPSearchResultCacheService) dsmCore.getService(DSMCore.SOAP_SEARCH);
	}

	/** The Spelling Checker singleton instance */
	public static SpellCheckService _SpellCheckService() {
		return (SpellCheckService) dsmCore.getService(DSMCore.SPELL_CHECK);
	}

	/** Returns the properly formatted next timestamp for logs */
	public static String getNextTimeStamp() {
		return new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").format(new Date());
	}

	/**
	 * Logs a search (or paging request) to the database.
	 * 
	 * @param job
	 *            details are needed from all tasks in the search to create the
	 *            log entry
	 * @param request
	 *            details are needed from the HTTP request to create the log
	 *            entry
	 */
	public static void logSearchToDatabase(final SearchJob job, final HttpServletRequest request) {
		if (!PropertiesService.getBooleanProperty("log.search.terms.to.database", false) || !PropertiesService.getBooleanProperty("database.enabled", false)) {
			return;
		} else if (job == null) {
			return;
		}

		final SearchTermHistory searchTermHistoryEntry = new SearchTermHistory();
		searchTermHistoryEntry.setSearchQuery(job.getSearchCriteria().getQuery());
		searchTermHistoryEntry.setSearchSources(StringUtils.defaultIfEmpty(StringUtils.join(job.getSearchCriteria().getSources(), ","), ""));
		searchTermHistoryEntry.setSearchToken(job.getToken());
		searchTermHistoryEntry.setRequestUrl(request.getRequestURL() + (request.getQueryString() != null ? "?" + request.getQueryString() : ""));
		searchTermHistoryEntry.setResultCount(job.getTotalResultsSoFar());
		searchTermHistoryEntry.setQueryDuration((int) job.getTimeSpentSearchingSoFar());
		searchTermHistoryEntry.setIpAddress(request.getRemoteAddr());
		searchTermHistoryEntry.setUserAgent(request.getHeader("User-Agent"));
		searchTermHistoryEntry.setDateRecorded(new Date());

		// done in a separate thread so the search isn't slowed, and not via
		// au.edu.educationau.opensource.dsm.threadpool.ThreadPool since those
		// calls can wait/block
		new Thread("LogSearchToDatabaseThread") {
			public void run() {
				// try/catch to ensure logging is done
				try {
					Connection connection = null;
					try {
						connection = _DBConnectionService().getConnection("dsm4");
						PreparedStatement ps = connection.prepareStatement("insert into search_term_history (search_query, search_sources, search_token, request_url, result_count, query_duration, ip_address, user_agent, date_recorded)" + "     values (?, ?, ?, ?, ?, ?, ?, ?, ?)");
						ps.setString(1, searchTermHistoryEntry.getSearchQuery());
						ps.setString(2, searchTermHistoryEntry.getSearchSources());
						ps.setLong(3, searchTermHistoryEntry.getSearchToken());
						ps.setString(4, searchTermHistoryEntry.getRequestUrl());
						ps.setInt(5, searchTermHistoryEntry.getResultCount());
						ps.setInt(6, searchTermHistoryEntry.getQueryDuration());
						ps.setString(7, searchTermHistoryEntry.getIpAddress());
						ps.setString(8, searchTermHistoryEntry.getUserAgent());
						ps.setTimestamp(9, new Timestamp(searchTermHistoryEntry.getDateRecorded().getTime()));

						int rowsAffected = ps.executeUpdate();
						if (rowsAffected != 1) {
							throw new Exception("Expected rowcount of 1 from insert, instead received: " + rowsAffected);
						}
						ps.close();
					} finally {
						if (connection != null) {
							_DBConnectionService().freeConnection("dsm4", connection);
						}
					}
				} catch (Exception e) {
					logger.error("Insert failed for this object: " + searchTermHistoryEntry, e);
				}
			}
		}.start();
	}

	public static final void destroy() {

	}

}